有三个柱子,分别为 from、buffer、to。需要将 from 上的圆盘全部移动到 to 上,并且要保证小圆盘始终在大圆盘上。
这是一个经典的递归问题,分为三步求解:
① 将 n-1 个圆盘从 from -> buffer
② 将 1 个圆盘从 from -> to
③ 将 n-1 个圆盘从 buffer -> to
如果只有一个圆盘,那么只需要进行一次移动操作。
从上面的讨论可以知道,an = 2 * an-1 + 1,显然 an = 2n - 1,n 个圆盘需要移动 2n - 1 次。
publicclassHanoi { publicstaticvoidmove(intn, Stringfrom, Stringbuffer, Stringto) { if (n == 1) { System.out.println("from " + from + " to " + to); return; } move(n - 1, from, to, buffer); move(1, from, buffer, to); move(n - 1, buffer, from, to); } publicstaticvoidmain(String[] args) { Hanoi.move(3, "H1", "H2", "H3"); } }
from H1 to H3 from H1 to H2 from H3 to H2 from H1 to H3 from H2 to H1 from H2 to H3 from H1 to H3
根据数据出现的频率对数据进行编码,从而压缩原始数据。
例如对于一个文本文件,其中各种字符出现的次数如下:
- a : 10
- b : 20
- c : 40
- d : 80
可以将每种字符转换成二进制编码,例如将 a 转换为 00,b 转换为 01,c 转换为 10,d 转换为 11。这是最简单的一种编码方式,没有考虑各个字符的权值(出现频率)。而哈夫曼编码采用了贪心策略,使出现频率最高的字符的编码最短,从而保证整体的编码长度最短。
首先生成一颗哈夫曼树,每次生成过程中选取频率最少的两个节点,生成一个新节点作为它们的父节点,并且新节点的频率为两个节点的和。选取频率最少的原因是,生成过程使得先选取的节点位于树的更低层,那么需要的编码长度更长,频率更少可以使得总编码长度更少。
生成编码时,从根节点出发,向左遍历则添加二进制位 0,向右则添加二进制位 1,直到遍历到叶子节点,叶子节点代表的字符的编码就是这个路径编码。
publicclassHuffman { privateclassNodeimplementsComparable<Node> { charch; intfreq; booleanisLeaf; Nodeleft, right; publicNode(charch, intfreq) { this.ch = ch; this.freq = freq; isLeaf = true; } publicNode(Nodeleft, Noderight, intfreq) { this.left = left; this.right = right; this.freq = freq; isLeaf = false; } @OverridepublicintcompareTo(Nodeo) { returnthis.freq - o.freq; } } publicMap<Character, String> encode(Map<Character, Integer> frequencyForChar) { PriorityQueue<Node> priorityQueue = newPriorityQueue<>(); for (Characterc : frequencyForChar.keySet()) { priorityQueue.add(newNode(c, frequencyForChar.get(c))); } while (priorityQueue.size() != 1) { Nodenode1 = priorityQueue.poll(); Nodenode2 = priorityQueue.poll(); priorityQueue.add(newNode(node1, node2, node1.freq + node2.freq)); } returnencode(priorityQueue.poll()); } privateMap<Character, String> encode(Noderoot) { Map<Character, String> encodingForChar = newHashMap<>(); encode(root, "", encodingForChar); returnencodingForChar; } privatevoidencode(Nodenode, Stringencoding, Map<Character, String> encodingForChar) { if (node.isLeaf) { encodingForChar.put(node.ch, encoding); return; } encode(node.left, encoding + '0', encodingForChar); encode(node.right, encoding + '1', encodingForChar); } }